package com.apikey.auth.service.impl;

import com.apikey.auth.entity.AccessLog;
import com.apikey.auth.repository.AccessLogRepository;
import com.apikey.auth.service.AccessLogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

import javax.persistence.criteria.Predicate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * 访问日志服务实现类
 * 提供访问日志的管理和查询功能的具体实现
 *
 * @author API Key Framework
 * @version 1.0
 */
@Service
public class AccessLogServiceImpl implements AccessLogService {

    @Autowired
    private AccessLogRepository accessLogRepository;

    /**
     * 记录访问日志
     *
     * @param accessLog 访问日志信息
     * @return 保存的访问日志
     */
    @Override
    public AccessLog logAccess(AccessLog accessLog) {
        if (accessLog.getAccessTime() == null) {
            accessLog.setAccessTime(LocalDateTime.now());
        }
        return accessLogRepository.save(accessLog);
    }

    /**
     * 根据ID查找访问日志
     *
     * @param id 访问日志ID
     * @return 访问日志
     */
    @Override
    public Optional<AccessLog> findById(Long id) {
        return accessLogRepository.findById(id);
    }

    /**
     * 分页查询访问日志
     *
     * @param apiKeyValue API Key值
     * @param clientIp    客户端IP
     * @param status      状态码
     * @param method      请求方法
     * @param startTime   开始时间
     * @param endTime     结束时间
     * @param pageable    分页参数
     * @return 分页结果
     */
    @Override
    public Page<AccessLog> findAccessLogs(String apiKeyValue, String clientIp, Integer status,
                                          String method, LocalDateTime startTime, LocalDateTime endTime,
                                          Pageable pageable) {

        Specification<AccessLog> spec = (root, query, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();

            if (apiKeyValue != null && !apiKeyValue.trim().isEmpty()) {
                predicates.add(criteriaBuilder.like(root.get("apiKeyValue"), "%" + apiKeyValue + "%"));
            }

            if (clientIp != null && !clientIp.trim().isEmpty()) {
                predicates.add(criteriaBuilder.like(root.get("clientIp"), "%" + clientIp + "%"));
            }

            if (status != null) {
                predicates.add(criteriaBuilder.equal(root.get("responseStatus"), status));
            }

            if (method != null && !method.trim().isEmpty()) {
                predicates.add(criteriaBuilder.equal(root.get("requestMethod"), method));
            }

            if (startTime != null) {
                predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get("accessTime"), startTime));
            }

            if (endTime != null) {
                predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get("accessTime"), endTime));
            }

            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        };

        return accessLogRepository.findAll(spec, pageable);
    }

    /**
     * 获取总访问次数
     *
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return 总访问次数
     */
    @Override
    public long getTotalCount(LocalDateTime startTime, LocalDateTime endTime) {
        return accessLogRepository.countByAccessTimeBetweenAndIsDeletedFalse(startTime, endTime);
    }

    /**
     * 获取成功访问次数
     *
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return 成功访问次数
     */
    @Override
    public long getSuccessfulCount(LocalDateTime startTime, LocalDateTime endTime) {
        return accessLogRepository.countByResponseStatusBetweenAndAccessTimeBetweenAndIsDeletedFalse(200, 299, startTime, endTime);
    }

    /**
     * 获取错误访问次数
     *
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return 错误访问次数
     */
    @Override
    public long getErrorCount(LocalDateTime startTime, LocalDateTime endTime) {
        return accessLogRepository.countByResponseStatusGreaterThanEqualAndAccessTimeBetweenAndIsDeletedFalse(400, startTime, endTime);
    }

    /**
     * 获取平均响应时间
     *
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return 平均响应时间（毫秒）
     */
    @Override
    public double getAverageResponseTime(LocalDateTime startTime, LocalDateTime endTime) {
        Double avgTime = accessLogRepository.findAverageResponseTimeByAccessTimeBetween(startTime, endTime);
        return avgTime != null ? avgTime : 0.0;
    }

    /**
     * 获取访问趋势数据
     *
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @param interval  时间间隔
     * @return 趋势数据
     */
    @Override
    public Map<String, Object> getAccessTrends(LocalDateTime startTime, LocalDateTime endTime, String interval) {
        Map<String, Object> trends = new HashMap<>();

        // 根据时间间隔生成时间点
        List<String> labels = new ArrayList<>();
        List<Long> data = new ArrayList<>();

        LocalDateTime current = startTime;
        DateTimeFormatter formatter;

        switch (interval.toLowerCase()) {
            case "hour":
                formatter = DateTimeFormatter.ofPattern("MM-dd HH:mm");
                while (current.isBefore(endTime)) {
                    LocalDateTime next = current.plusHours(1);
                    labels.add(current.format(formatter));
                    data.add(accessLogRepository.countByAccessTimeBetweenAndIsDeletedFalse(current, next));
                    current = next;
                }
                break;
            case "day":
                formatter = DateTimeFormatter.ofPattern("MM-dd");
                while (current.isBefore(endTime)) {
                    LocalDateTime next = current.plusDays(1);
                    labels.add(current.format(formatter));
                    data.add(accessLogRepository.countByAccessTimeBetweenAndIsDeletedFalse(current, next));
                    current = next;
                }
                break;
            case "week":
                formatter = DateTimeFormatter.ofPattern("MM-dd");
                while (current.isBefore(endTime)) {
                    LocalDateTime next = current.plusWeeks(1);
                    labels.add(current.format(formatter));
                    data.add(accessLogRepository.countByAccessTimeBetween(current, next));
                    current = next;
                }
                break;
            default:
                throw new IllegalArgumentException("不支持的时间间隔: " + interval);
        }

        trends.put("labels", labels);
        trends.put("data", data);

        return trends;
    }

    /**
     * 获取状态码分布
     *
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return 状态码分布
     */
    @Override
    public Map<String, Object> getStatusDistribution(LocalDateTime startTime, LocalDateTime endTime) {
        Map<String, Object> distribution = new HashMap<>();

        // 获取状态码分布数据
        List<Object[]> statusCounts = accessLogRepository.findStatusDistributionByAccessTimeBetween(startTime, endTime);

        List<String> labels = new ArrayList<>();
        List<Long> data = new ArrayList<>();

        for (Object[] row : statusCounts) {
            Integer status = (Integer) row[0];
            Long count = (Long) row[1];

            labels.add(getStatusLabel(status));
            data.add(count);
        }

        distribution.put("labels", labels);
        distribution.put("data", data);

        return distribution;
    }

    /**
     * 导出访问日志为CSV
     *
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @param apiKeyId  API Key ID过滤
     * @param clientIp  客户端IP过滤
     * @return CSV数据
     */
    @Override
    public String exportToCsv(LocalDateTime startTime, LocalDateTime endTime, Long apiKeyId, String clientIp) {
        // 构建查询条件
        Specification<AccessLog> spec = (root, query, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            predicates.add(criteriaBuilder.equal(root.get("isDeleted"), false));

            if (apiKeyId != null) {
                predicates.add(criteriaBuilder.equal(root.get("apiKeyId"), apiKeyId));
            }
            if (startTime != null) {
                predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get("accessTime"), startTime));
            }
            if (endTime != null) {
                predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get("accessTime"), endTime));
            }
            if (clientIp != null && !clientIp.isEmpty()) {
                predicates.add(criteriaBuilder.equal(root.get("clientIp"), clientIp));
            }

            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        };

        List<AccessLog> logs = accessLogRepository.findAll(spec);
        return exportToCsv(logs);
    }

    /**
     * 清理历史访问日志
     *
     * @param retentionDays 保留天数
     * @return 清理的记录数
     */
    @Override
    public int cleanupOldAccessLogs(int retentionDays) {
        LocalDateTime cutoffTime = LocalDateTime.now().minusDays(retentionDays);
        return accessLogRepository.deleteByAccessTimeBefore(cutoffTime);
    }

    /**
     * 多条件分页查询访问日志
     *
     * @param apiKeyId       API Key ID过滤
     * @param startTime      开始时间
     * @param endTime        结束时间
     * @param clientIp       客户端IP过滤
     * @param responseStatus 响应状态过滤
     * @param pageable       分页参数
     * @return 访问日志分页结果
     */
    @Override
    public Page<AccessLog> getAccessLogs(Long apiKeyId, LocalDateTime startTime, LocalDateTime endTime,
                                         String clientIp, Integer responseStatus, Pageable pageable) {
        // 构建查询条件
        // 构建查询条件
        Specification<AccessLog> spec = (root, query, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            predicates.add(criteriaBuilder.equal(root.get("isDeleted"), false));

            if (apiKeyId != null) {
                predicates.add(criteriaBuilder.equal(root.get("apiKeyId"), apiKeyId));
            }
            if (startTime != null) {
                predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get("accessTime"), startTime));
            }
            if (endTime != null) {
                predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get("accessTime"), endTime));
            }
            if (clientIp != null && !clientIp.isEmpty()) {
                predicates.add(criteriaBuilder.equal(root.get("clientIp"), clientIp));
            }
            if (responseStatus != null) {
                predicates.add(criteriaBuilder.equal(root.get("responseStatus"), responseStatus));
            }

            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        };

        return accessLogRepository.findAll(spec, pageable);
    }

    /**
     * 多条件分页查询访问日志（排除管理员请求）
     *
     * @param apiKeyId       API Key ID过滤
     * @param startTime      开始时间
     * @param endTime        结束时间
     * @param clientIp       客户端IP过滤
     * @param responseStatus 响应状态过滤
     * @param pageable       分页参数
     * @return 访问日志分页结果
     */
    @Override
    public Page<AccessLog> getAccessLogsExcludingAdmin(Long apiKeyId, LocalDateTime startTime, LocalDateTime endTime,
                                                       String clientIp, Integer responseStatus, Pageable pageable) {
        // 构建查询条件，排除ADMIN_REQUEST类型的记录
        Specification<AccessLog> spec = (root, query, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            predicates.add(criteriaBuilder.equal(root.get("isDeleted"), false));
            // 排除ADMIN_REQUEST类型的记录
            predicates.add(criteriaBuilder.notEqual(root.get("apiKeyValue"), "ADMIN_REQUEST"));

            if (apiKeyId != null) {
                predicates.add(criteriaBuilder.equal(root.get("apiKeyId"), apiKeyId));
            }
            if (startTime != null) {
                predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get("accessTime"), startTime));
            }
            if (endTime != null) {
                predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get("accessTime"), endTime));
            }
            if (clientIp != null && !clientIp.isEmpty()) {
                predicates.add(criteriaBuilder.equal(root.get("clientIp"), clientIp));
            }
            if (responseStatus != null) {
                predicates.add(criteriaBuilder.equal(root.get("responseStatus"), responseStatus));
            }

            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        };

        return accessLogRepository.findAll(spec, pageable);
    }

    /**
     * 搜索访问日志
     *
     * @param keyword  关键字
     * @param pageable 分页参数
     * @return 访问日志分页结果
     */
    @Override
    public Page<AccessLog> searchAccessLogs(String keyword, Pageable pageable) {
        return accessLogRepository.findByRequestPathContainingOrClientIpContainingOrUserAgentContaining(
                keyword, keyword, keyword, pageable);
    }

    /**
     * 获取总访问次数
     *
     * @return 总访问次数
     */
    @Override
    public long getTotalAccessCount() {
        return accessLogRepository.count();
    }

    /**
     * 获取今日访问次数
     *
     * @return 今日访问次数
     */
    @Override
    public long getTodayAccessCount() {
        LocalDateTime startOfDay = LocalDateTime.now().withHour(0).withMinute(0).withSecond(0).withNano(0);
        LocalDateTime endOfDay = startOfDay.plusDays(1);
        return accessLogRepository.countByAccessTimeBetweenAndIsDeletedFalse(startOfDay, endOfDay);
    }

    /**
     * 获取成功率
     *
     * @return 成功率（百分比）
     */
    @Override
    public double getSuccessRate() {
        long totalCount = accessLogRepository.count();
        if (totalCount == 0) {
            return 0.0;
        }
        long successCount = accessLogRepository.countByResponseStatusBetweenAndIsDeletedFalse(200, 299);
        return (double) successCount / totalCount * 100;
    }

    /**
     * 获取平均响应时间
     *
     * @return 平均响应时间（毫秒）
     */
    @Override
    public double getAverageResponseTime() {
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime startOfDay = now.withHour(0).withMinute(0).withSecond(0).withNano(0);
        Double avgTime = accessLogRepository.findAverageResponseTimeByAccessTimeBetween(startOfDay, now);
        return avgTime != null ? avgTime : 0.0;
    }

    /**
     * 获取热门API Key统计
     *
     * @param limit 返回数量限制
     * @return 热门API Key列表
     */
    @Override
    public List<Map<String, Object>> getTopApiKeys(int limit) {
        LocalDateTime endTime = LocalDateTime.now();
        LocalDateTime startTime = endTime.minusDays(7); // 最近7天
        List<Object[]> results = accessLogRepository.findTopApiKeys(startTime, endTime);
        List<Map<String, Object>> topApiKeys = new ArrayList<>();

        int count = 0;
        for (Object[] row : results) {
            if (count >= limit) break;
            Map<String, Object> item = new HashMap<>();
            item.put("apiKeyValue", row[0]);
            item.put("accessCount", row[1]);
            topApiKeys.add(item);
            count++;
        }

        return topApiKeys;
    }

    /**
     * 获取热门访问路径统计
     *
     * @param limit 返回数量限制
     * @return 热门路径列表
     */
    @Override
    public List<Map<String, Object>> getTopPaths(int limit) {
        LocalDateTime endTime = LocalDateTime.now();
        LocalDateTime startTime = endTime.minusDays(7); // 最近7天
        List<Object[]> results = accessLogRepository.findTopPaths(startTime, endTime);
        List<Map<String, Object>> topPaths = new ArrayList<>();

        int count = 0;
        for (Object[] row : results) {
            if (count >= limit) break;
            Map<String, Object> item = new HashMap<>();
            item.put("requestPath", row[0]);
            item.put("accessCount", row[1]);
            topPaths.add(item);
            count++;
        }

        return topPaths;
    }

    /**
     * 获取热门客户端IP统计
     *
     * @param limit 返回数量限制
     * @return 热门IP列表
     */
    @Override
    public List<Map<String, Object>> getTopClientIps(int limit) {
        LocalDateTime endTime = LocalDateTime.now();
        LocalDateTime startTime = endTime.minusDays(7); // 最近7天
        List<Object[]> results = accessLogRepository.findTopClientIps(startTime, endTime);
        List<Map<String, Object>> topClientIps = new ArrayList<>();

        int count = 0;
        for (Object[] row : results) {
            if (count >= limit) break;
            Map<String, Object> item = new HashMap<>();
            item.put("clientIp", row[0]);
            item.put("accessCount", row[1]);
            topClientIps.add(item);
            count++;
        }

        return topClientIps;
    }

    /**
     * 获取每日访问统计
     *
     * @param days 统计天数
     * @return 每日统计列表
     */
    @Override
    public List<Map<String, Object>> getDailyStats(int days) {
        LocalDateTime endTime = LocalDateTime.now();
        LocalDateTime startTime = endTime.minusDays(days);

        List<Object[]> results = accessLogRepository.findDailyStats(startTime, endTime);
        List<Map<String, Object>> dailyStats = new ArrayList<>();

        for (Object[] row : results) {
            Map<String, Object> item = new HashMap<>();
            item.put("date", row[0]);
            item.put("accessCount", row[1]);
            dailyStats.add(item);
        }

        return dailyStats;
    }

    /**
     * 获取每小时访问统计
     *
     * @param hours 统计小时数
     * @return 每小时统计列表
     */
    @Override
    public List<Map<String, Object>> getHourlyStats(int hours) {
        LocalDateTime endTime = LocalDateTime.now();
        LocalDateTime startTime = endTime.minusHours(hours);

        List<Object[]> results = accessLogRepository.findHourlyStats(startTime, endTime);
        List<Map<String, Object>> hourlyStats = new ArrayList<>();

        for (Object[] row : results) {
            Map<String, Object> item = new HashMap<>();
            item.put("hour", row[0]);
            item.put("accessCount", row[1]);
            hourlyStats.add(item);
        }

        return hourlyStats;
    }

    /**
     * 获取访问日志统计信息
     *
     * @return 统计信息Map
     */
    @Override
    public Map<String, Object> getAccessLogStats() {
        Map<String, Object> stats = new HashMap<>();

        // 基础统计
        stats.put("totalAccess", getTotalAccessCount());
        stats.put("todayAccess", getTodayAccessCount());
        stats.put("successRate", getSuccessRate());
        stats.put("avgResponseTime", getAverageResponseTime());

        // 时间范围统计
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime startOfDay = now.withHour(0).withMinute(0).withSecond(0).withNano(0);
        LocalDateTime startOfWeek = now.minusDays(7);
        LocalDateTime startOfMonth = now.minusDays(30);

        stats.put("todayCount", getTotalCount(startOfDay, now));
        stats.put("weekCount", getTotalCount(startOfWeek, now));
        stats.put("monthCount", getTotalCount(startOfMonth, now));

        // 成功和错误统计
        stats.put("successCount", getSuccessfulCount(startOfMonth, now));
        stats.put("errorCount", getErrorCount(startOfMonth, now));

        return stats;
    }

    /**
     * 根据客户端IP查询访问日志
     *
     * @param clientIp 客户端IP
     * @param pageable 分页参数
     * @return 访问日志分页结果
     */
    @Override
    public Page<AccessLog> findByClientIp(String clientIp, Pageable pageable) {
        return accessLogRepository.findByClientIpAndIsDeletedFalse(clientIp, pageable);
    }

    /**
     * 根据响应状态查询访问日志
     *
     * @param responseStatus 响应状态
     * @param pageable       分页参数
     * @return 访问日志分页结果
     */
    @Override
    public Page<AccessLog> findByResponseStatus(Integer responseStatus, Pageable pageable) {
        return accessLogRepository.findByResponseStatusAndIsDeletedFalse(responseStatus, pageable);
    }

    /**
     * 导出为CSV格式
     *
     * @param logs 访问日志列表
     * @return CSV数据
     */
    private String exportToCsv(List<AccessLog> logs) {
        StringBuilder csv = new StringBuilder();

        // 写入CSV头部
        csv.append("ID,API Key,请求路径,请求方法,客户端IP,用户代理,响应状态,响应时间(ms),访问时间\n");

        // 写入数据
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        for (AccessLog log : logs) {
            csv.append(String.format("%d,%s,%s,%s,%s,%s,%d,%d,%s\n",
                    log.getId(),
                    log.getApiKeyValue(),
                    log.getRequestPath(),
                    log.getRequestMethod(),
                    log.getClientIp(),
                    log.getUserAgent(),
                    log.getResponseStatus(),
                    log.getResponseTime(),
                    log.getAccessTime().format(formatter)
            ));
        }

        return csv.toString();
    }

    /**
     * 获取状态码标签
     *
     * @param status 状态码
     * @return 状态码标签
     */
    private String getStatusLabel(Integer status) {
        if (status >= 200 && status < 300) {
            return "2xx 成功";
        } else if (status >= 300 && status < 400) {
            return "3xx 重定向";
        } else if (status >= 400 && status < 500) {
            return "4xx 客户端错误";
        } else if (status >= 500) {
            return "5xx 服务器错误";
        } else {
            return "其他";
        }
    }
}